#include <qstyle.h>
#include <qstyleoption.h>

#include "ZoomScrollBar.h"

ZoomScrollBar::ZoomScrollBar(QWidget * parent) : QScrollBar(parent)
{
    init();
}

ZoomScrollBar::ZoomScrollBar(Qt::Orientation o, QWidget *parent) : QScrollBar(o, parent)
{
    init();
}

ZoomScrollBar::ZoomScrollBar(double minBase, double maxBase,
        Qt::Orientation o, QWidget *parent):
    QScrollBar(o, parent)
{
    init();
    setBase(minBase, maxBase);
    moveSlider(minBase, maxBase);
}

void ZoomScrollBar::init()
{
    _inverted = orientation() == Qt::Vertical;
    _baseTicks = 1000000;
    _minBase = 0.0;
    _maxBase = 1.0;
    moveSlider(_minBase, _maxBase);

    connect(this, SIGNAL(sliderMoved(int)), SLOT(catchSliderMoved(int)));
    connect(this, SIGNAL(valueChanged(int)), SLOT(catchValueChanged(int)));
}

void ZoomScrollBar::setInverted(bool inverted)
{
    if (_inverted != inverted)
    {
        _inverted = inverted;
        moveSlider(minSliderValue(), maxSliderValue());
    }
}

bool ZoomScrollBar::isInverted() const
{
    return _inverted;
}

void ZoomScrollBar::setBase(double min, double max)
{
    if (min != _minBase || max != _maxBase)
    {
        _minBase = min;
        _maxBase = max;

        moveSlider(minSliderValue(), maxSliderValue());
    }
}

void ZoomScrollBar::moveSlider(double min, double max)
{
    const int sliderTicks = qRound((max - min) /
        (_maxBase - _minBase) * _baseTicks);

    // setRange initiates a valueChanged of the scrollbars
    // in some situations. So we block
    // and unblock the signals.

    blockSignals(true);

    setRange(sliderTicks / 2, _baseTicks - sliderTicks / 2);
    int steps = sliderTicks / 200;
    if (steps <= 0)
        steps = 1;

    setSingleStep(steps);
    setPageStep(sliderTicks);

    int tick = mapToTick(min + (max - min) / 2);
    if (isInverted())
        tick = _baseTicks - tick;

    setSliderPosition(tick);
    blockSignals(false);
}

double ZoomScrollBar::minBaseValue() const
{
    return _minBase;
}

double ZoomScrollBar::maxBaseValue() const
{
    return _maxBase;
}

void ZoomScrollBar::sliderRange(int value, double &min, double &max) const
{
    if (isInverted())
        value = _baseTicks - value;

    const int visibleTicks = pageStep();

    min = mapFromTick(value - visibleTicks / 2);
    max = mapFromTick(value + visibleTicks / 2);
}

double ZoomScrollBar::minSliderValue() const
{
    double min, dummy;
    sliderRange(value(), min, dummy);

    return min;
}

double ZoomScrollBar::maxSliderValue() const
{
    double max, dummy;
    sliderRange(value(), dummy, max);

    return max;
}

int ZoomScrollBar::mapToTick(double v) const
{
    const double pos = (v - _minBase) / (_maxBase - _minBase) * _baseTicks;
    return static_cast<int>(pos);
}

double ZoomScrollBar::mapFromTick(int tick) const
{
    return _minBase + (_maxBase - _minBase) * tick / _baseTicks;
}

void ZoomScrollBar::catchValueChanged(int value)
{
    double min, max;
    sliderRange(value, min, max);
    Q_EMIT valueChanged(orientation(), min, max);
}

void ZoomScrollBar::catchSliderMoved(int value)
{
    double min, max;
    sliderRange(value, min, max);
    Q_EMIT sliderMoved(orientation(), min, max);
}

int ZoomScrollBar::extent() const
{
    QStyleOptionSlider opt;
    opt.init(this);
    opt.subControls = QStyle::SC_None;
    opt.activeSubControls = QStyle::SC_None;
    opt.orientation = orientation();
    opt.minimum = minimum();
    opt.maximum = maximum();
    opt.sliderPosition = sliderPosition();
    opt.sliderValue = value();
    opt.singleStep = singleStep();
    opt.pageStep = pageStep();
    opt.upsideDown = invertedAppearance();

    if (orientation() == Qt::Horizontal)
        opt.state |= QStyle::State_Horizontal;
    return style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, this);
}
